# @Time : 2022/5/10 15:00 

# @Author : 朱泽玉

# @File : update.py

# @Software: PyCharm

# @Description: 

# Modified:
import datetime
import string
import time

import pymysql
import requests
import cv2
import numpy as np
import os
from PIL import ImageFont, ImageDraw, Image

DB = pymysql.connect(host="121.36.46.69", port=3306, user="root", password="zzyy0223", database="education",
                     charset='utf8')
CURSOR = DB.cursor()

IMG_SRC = 'https://127.0.0.1:22129/img/'
PATH = './data'
# 调用熟悉的人脸分类器
DETECTOR = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
FONT_PATH = "./simsun.ttc"  # <== 这里是宋体路径
FONT = ImageFont.truetype(FONT_PATH, 32)


# 从接口获取图片
def get_img():
    sql_st = "select uid from img_face"
    cursor = CURSOR
    cursor.execute(sql_st)
    img_list = list(cursor.fetchall())
    for index in img_list:
        index = index[0]
        sql_st = "select account from img_face where uid = '%s'" % index
        cursor.execute(sql_st)
        account_id = list(cursor.fetchall())[0][0]
        img_path = IMG_SRC + '' + str(account_id)
        img = requests.get(img_path, verify=False).content
        with open(PATH + '/User.' + str(index - 1) + '.1.jpg', 'wb') as s:
            s.write(img)


# 获取图片样例以及对应的index
def get_images_and_labels(path):
    image_paths = [os.path.join(path, f) for f in os.listdir(path)]
    # 新建连个list用于存放
    face_samples = []
    ids = []
    # 遍历图片路径，导入图片和id添加到list中
    for image_path in image_paths:
        # 通过图片路径将其转换为灰度图片
        img = Image.open(image_path).convert('L')
        # 将图片转化为数组
        img_np = np.array(img, 'uint8')
        if os.path.split(image_path)[-1].split(".")[-1] != 'jpg':
            continue
        # 为了获取id，将图片和路径分裂并获取
        index_id = int(os.path.split(image_path)[-1].split(".")[1])
        faces = DETECTOR.detectMultiScale(img_np)
        # 将获取的图片和id添加到list中
        for (x, y, w, h) in faces:
            face_samples.append(img_np[y:y + h, x:x + w])
            ids.append(index_id)
    return face_samples, ids


# 训练模型
def train():
    # 初始化识别的方法
    recognition = cv2.face.LBPHFaceRecognizer_create()

    # 调用函数并将数据喂给识别器训练
    print('Training...')
    faces, ids = get_images_and_labels(PATH)
    # 训练模型
    recognition.train(faces, np.array(ids))
    # 保存模型
    recognition.save('trainer.yml')


def recognize():
    cursor = CURSOR
    # 准备好识别方法
    recognizer = cv2.face.LBPHFaceRecognizer_create()

    # 使用之前训练好的模型
    recognizer.read('trainer.yml')

    # 再次调用人脸分类器
    cascade_path = "haarcascade_frontalface_default.xml"
    face_cascade = cv2.CascadeClassifier(cascade_path)
    # 调用摄像头
    cam = cv2.VideoCapture(0)
    minW = 0.1 * cam.get(3)
    minH = 0.1 * cam.get(4)
    while True:
        ret, img = cam.read()
        img = cv2.flip(img, 1)
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        # 识别人脸
        faces = face_cascade.detectMultiScale(
            gray,
            scaleFactor=1.2,
            minNeighbors=2,  # minNeighbors表示每一个目标至少要被检测到3次才算是真的目标(因为周围的像素和不同的窗口大小都可以检测到人脸)
            minSize=(int(minW), int(minH))
        )
        # 进行校验
        if len(faces) != 0:
            for (x, y, w, h) in faces:
                index, confidence = recognizer.predict(gray[y:y + h, x:x + w])
                # 计算出一个检验结果
                # confidence越小越容易识别不出，confidence越大容易误识别
                if confidence < 60:
                    # 查看上次进出时间
                    sql_st = "select date_time from img_face where uid = '%s'" % (
                            index + 1)
                    cursor.execute(sql_st)
                    start_time = list(cursor.fetchall())[0][0]
                    end_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                    start_sec = time.mktime(time.strptime(start_time, '%Y-%m-%d %H:%M:%S'))
                    end_sec = time.mktime(time.strptime(end_time, '%Y-%m-%d %H:%M:%S'))
                    # 不是在很短时间内（2min内多次均为一次进出）或者一段时间以后的话，不允许进出
                    print(end_sec - start_sec)
                    print(index)
                    if end_sec - start_sec > 600 or end_sec - start_sec < 150:
                        # 从数据库中获取识别人身份
                        sql_st = "select * from account where uid = ( select account from img_face where uid = '%s')" % (
                                index + 1)
                        cursor.execute(sql_st)
                        account = list(cursor.fetchall())
                        name = account[0][7]
                        uid = account[0][0]
                        # 如果是一段时间以后的话，在出入记录表中插入，并且在照片表中更新最近出入时间
                        if end_sec - start_sec > 600:
                            sql_st = "insert into record(date_time,method,student_uid) values ('%s','%s','%s')" % (
                                datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 1, uid)
                            cursor.execute(sql_st)
                            DB.commit()
                            sql_st = "update img_face set date_time = '%s'" % (
                                datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
                            cursor.execute(sql_st)
                            DB.commit()
                        cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
                        img_pil = Image.fromarray(img)
                        draw = ImageDraw.Draw(img_pil)
                        draw.text((x + 5, y - 50), str(name), font=FONT, fill=(0, 255, 0, 0))
                        img = np.array(img_pil)
                cv2.imshow('camera', img)
                # confidence = "{0}%", format(round(100 - confidence))
                # else:
                # name = "unknown"
                # confidence = "{0}%", format(round(100 - confidence))
                # 展示结果
        else:
            cv2.imshow('camera', img)

        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    # 释放资源
    cam.release()
    cv2.destroyAllWindows()


if __name__ == '__main__':
    if os.path.exists(PATH):
        pass
    else:
        os.mkdir(PATH)
    get_img()
    train()
    recognize()
